﻿using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
using MimeKit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;

namespace Publicuse.Util
{
    /// <summary>
    /// 邮件收发工具类
    /// </summary>
    public class EmailUtil
    {
        private static EmailUtil emailUtil = null;

        private EmailUtil()
        {

        }

        static EmailUtil()
        {
            emailUtil = new EmailUtil();
        }

        public static EmailUtil GetEmailUtil { get { return emailUtil; } }

        /// <summary>
        /// 发送邮件
        /// </summary>
        /// <param name="fromEmail">发件人邮箱</param>
        /// <param name="toEmail">收件人邮箱</param>
        /// <param name="ckcode">发件人邮箱授权码</param>
        /// <param name="subject">邮件标题</param>
        /// <param name="body">邮件内容</param>
        /// <param name="attachmentUrl">邮件附件</param>
        public void SendEmail(string fromEmail, string toEmail, string ckcode, string subject, string body, bool isqq, List<string> attachMentUrl = null)
        {
            //实例化一个发送邮件类
            MailMessage mailMessage = new MailMessage();
            //发件人邮箱地址，方法重载不同，可以根据需求自行选择。
            mailMessage.From = new MailAddress(fromEmail, DateTime.Now.ToString("yyyy-MM-dd"));
            //邮件标题
            mailMessage.Subject = subject;
            //邮件标题编码   
            mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
            //邮件内容编码       
            mailMessage.BodyEncoding = Encoding.UTF8;
            //是否是HTML邮件    
            mailMessage.IsBodyHtml = true;
            //邮件优先级 
            mailMessage.Priority = MailPriority.High;
            //邮件内容
            mailMessage.Body = body;
            if (attachMentUrl != null)
            {
                //添加邮件附件
                //string attachMentUrl = AppDomain.CurrentDomain.BaseDirectory + "我的邮件测试.txt";
                foreach (var item in attachMentUrl)
                {
                    Attachment attachment = new Attachment(item);
                    mailMessage.Attachments.Add(attachment);
                }
            }
            //实例化一个SmtpClient类
            SmtpClient client = new SmtpClient();
            //使用的是qq邮箱，所以是smtp.qq.com，如果你使用的是126邮箱，那么就是smtp.126.com
            //Gmail使用的Host:smtp.gmail.com Gmail使用的端口587
            if (isqq)
            {
                client.Host = "smtp.qq.com";
                client.UseDefaultCredentials = false;
            }
            else if (!isqq)
            {
                client.Host = "smtp.163.com";
                client.UseDefaultCredentials = true;
            }
            //使用安全加密连接
            client.EnableSsl = true;
            //不和请求一块发送
            //验证发件人身份(发件人的邮箱，邮箱里的生成授权码) // 1498799985@qq.com ffqxjsyulhjtfeaf
            client.Credentials = new NetworkCredential(fromEmail, ckcode);
            try
            {
                //添加邮件接收人地址
                string[] receivers = toEmail.Split(new char[] { ',' });
                Array.ForEach(receivers.ToArray(), ToMail => { mailMessage.To.Add(ToMail); });
                //发送
                client.SendAsync(mailMessage, mailMessage);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 收QQ邮件
        /// </summary>
        /// <param name="user">收件人邮箱</param>
        /// <param name="ckcode">收件人邮箱授权码</param>
        public void GetQQEmail(string user, string pwd, string day, string fileName, out string msg, out List<string> fileList)
        {
            fileList = new List<string>();
            msg = string.Empty;
            string account = user;
            string passWord = pwd;//获得的授权码
            //准备工作结束
            ImapClient client = new ImapClient();

            #region 连接到邮件服务器
            try
            {
                //一、创建获取邮件客户端并连接到邮件服务器。
                //带端口号和协议的连接方式
                client.Connect("imap.qq.com", 993, true);
            }
            catch (ImapCommandException ex)
            {
                msg = "尝试连接时出错：" + ex.Message;
            }
            catch (ImapProtocolException ex)
            {
                msg = "尝试连接时的协议错误：" + ex.Message;
            }
            catch (Exception ex)
            {
                msg = "服务器连接错误：" + ex.Message;
            }

            try
            {
                // 二、验证登录信息，输入账号和密码登录。
                client.Authenticate(account, passWord);
            }
            catch (AuthenticationException ex)
            {
                msg = "无效的用户名或密码：" + ex.Message;
            }
            catch (ImapCommandException ex)
            {
                msg = "尝试验证错误：" + ex.Message;
            }
            catch (ImapProtocolException ex)
            {
                msg = "尝试验证时的协议错误：" + ex.Message;
            }
            catch (Exception ex)
            {
                msg = "账户认证错误：" + ex.Message;
            }
            #endregion
            //获取所有的文件夹
            List<IMailFolder> mailFolderList = client.GetFolders(client.PersonalNamespaces[0]).ToList();
            if (mailFolderList != null)
            {
                if (mailFolderList.Count > 0)
                {
                    //只获取收件箱文件夹
                    IMailFolder folder = mailFolderList.Find(o => o.FullName.ToUpper() == "INBOX");
                    //四、从文件夹获取文件
                    //打开文件夹并设置为读的方式
                    folder.Open(MailKit.FolderAccess.ReadOnly);

                    //获取大于所选时间的所有邮件的唯一Id
                    var uidss = folder.Search(SearchQuery.DeliveredAfter(DateTime.Parse(day)));

                    //获取邮件头
                    var MailHeaders = folder.Fetch(uidss, MessageSummaryItems.UniqueId | MessageSummaryItems.Full);
                    //同主题邮件取日期最新
                    var NeedMailIds = from a in MailHeaders
                                      group a by a.NormalizedSubject into grp
                                      let maxTime = grp.Max(a => a.Date.DateTime)
                                      from row in grp
                                      where row.Date.DateTime == maxTime
                                      select row.UniqueId;

                    if (NeedMailIds != null)
                    {
                        //获取邮件信息
                        foreach (var item in NeedMailIds)
                        {
                            MimeMessage message = folder.GetMessage(item);
                            //提取该邮件所有普通附件
                            foreach (MimePart attachment in message.Attachments)
                            {
                                //下载附件
                                using (var cancel = new System.Threading.CancellationTokenSource())
                                {
                                    var fileDir = AppDomain.CurrentDomain.BaseDirectory + "//upload";
                                    if (!Directory.Exists(fileDir))
                                        Directory.CreateDirectory(fileDir);

                                    string filePath = fileDir + "/" + Guid.NewGuid().ToString() + attachment.FileName;
                                    using (var stream = File.Create(filePath))
                                    {
                                        attachment.ContentObject.DecodeTo(stream, cancel.Token);
                                        fileList.Add(filePath);
                                    }
                                }
                            }
                        }
                    }
                    //关闭文件夹
                    folder.Close();
                }
            }
        }

        /// <summary>
        ///  收163邮箱邮件
        /// </summary>
        /// <param name="user"></param>
        /// <param name="pwd"></param>
        /// <param name="day"></param>
        /// <param name="fileName"></param>
        /// <param name="msg"></param>
        /// <param name="fileList"></param>
        /// <param name="textBody"></param>
        public void Get163Email(string user, string pwd, string day, string fileName, out string msg, out List<string> fileList, out string textBody)
        {
            msg = string.Empty;
            textBody = string.Empty;
            fileList = new List<string>();
            ImapClient client = new ImapClient();
            string account = user;
            string passWord = pwd;
            ResetConnect(ref client, account, passWord, true, out msg);
            try
            {
                ResetConnect(ref client, account, passWord, false, out msg);

                //获取所有的文件夹
                List<IMailFolder> mailFolderList = client.GetFolders(client.PersonalNamespaces[0]).ToList();
                if (mailFolderList != null)
                {
                    if (mailFolderList.Count > 0)
                    {
                        //只获取收件箱文件夹
                        IMailFolder folder = mailFolderList.Find(o => o.FullName.ToUpper() == "INBOX");

                        //打开文件夹并设置为读的方式
                        folder.Open(MailKit.FolderAccess.ReadOnly);

                        //获取指定时间的所有邮件的唯一Id
                        var uidss = folder.Search(SearchQuery.DeliveredAfter(DateTime.Parse(day)));

                        //获取邮件头
                        folder.Fetch(uidss, MessageSummaryItems.UniqueId | MessageSummaryItems.Full);

                        //获取完整邮件
                        foreach (var item in uidss)
                        {
                            MimeMessage message = folder.GetMessage(new UniqueId(item.Id));
                            var fieln = fileName.Split(',');
                            var ishave = false;
                            foreach (var fil in fieln)
                            {
                                if (message.Subject.Contains(fil))
                                {
                                    ishave = true;
                                    break;
                                }
                            }
                            if (ishave)
                            {
                                textBody = message.TextBody;
                                foreach (MimePart attachment in message.Attachments)
                                {
                                    using (var cancel = new System.Threading.CancellationTokenSource())
                                    {
                                        var fileDir = AppDomain.CurrentDomain.BaseDirectory + "//upload";
                                        if (!Directory.Exists(fileDir))
                                            Directory.CreateDirectory(fileDir);

                                        string filePath = fileDir + "/" + Guid.NewGuid().ToString() + attachment.FileName;
                                        using (var stream = File.Create(filePath))
                                        {
                                            attachment.ContentObject.DecodeTo(stream, cancel.Token);
                                            fileList.Add(filePath);
                                        }
                                    }
                                }
                            }
                        }
                        folder.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                msg = string.Format("对象{0}异常{1}堆栈{2}", ex.Source, ex.Message, ex.StackTrace);
            }
            finally
            {
                client.Dispose();
            }
        }

        /// <summary>
        /// 连接客户端邮箱
        /// </summary>
        /// <param name="client"></param>
        /// <param name="account"></param>
        /// <param name="passWord"></param>
        /// <param name="isFirst"></param>
        /// <param name="msg"></param>
        private static void ResetConnect(ref ImapClient client, string account, string passWord, bool isFirst, out string msg)
        {
            msg = string.Empty;
            try
            {
                if (isFirst)
                {
                    client.Connect("imap.163.com", 993, true);
                }
                else
                {
                    if (!client.IsConnected)
                        client.Connect("imap.163.com", 993, true);
                }
            }
            catch (ImapCommandException ex)
            {
                msg = "尝试连接时出错:" + ex.Message;
            }
            catch (ImapProtocolException ex)
            {
                msg = "尝试连接时的协议错误:" + ex.Message;
            }
            catch (Exception ex)
            {
                msg = "服务器连接错误:" + ex.Message;
            }

            try
            {
                if (isFirst)
                {
                    client.Authenticate(account, passWord);
                }
                else
                {
                    if (!client.IsAuthenticated)
                        client.Authenticate(account, passWord);
                }
            }
            catch (AuthenticationException ex)
            {
                msg = "无效的用户名或密码:" + ex.Message;
            }
            catch (ImapCommandException ex)
            {
                msg = "尝试验证错误:" + ex.Message;
            }
            catch (ImapProtocolException ex)
            {
                msg = "尝试验证时的协议错误:" + ex.Message;
            }
            catch (Exception ex)
            {
                msg = "账户认证错误:" + ex.Message;
            }

            if (HasImapCapabilitiesId(client.Capabilities, ImapCapabilities.Id))
            {
                var clientImplementation = new ImapImplementation
                {
                    Name = "MeSince",
                    Version = "2.0"
                };
                var serverImplementation = client.Identify(clientImplementation);
            }
        }

        //mailkit还提供查询是否支持命令的语句，如果你不知道邮箱是否支持这个命令可以使用下面的函数来判断：
        private static bool HasImapCapabilitiesId(ImapCapabilities sourceFlag, ImapCapabilities targetFlag)
        {
            return ((sourceFlag | targetFlag) == sourceFlag);
        }
    }
}
